﻿using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Input;
using VideoTools.Extension;
using VideoTools.Util;
using VideoTools.vo;
using Log = Serilog.Log;

namespace VideoTools.Commands
{
    public class AsciiCommands : NotifyPropertyBase
    {
        private static volatile AsciiCommands _instance;
        private static readonly object SyncRoot = new object();
        private string xfps = null;
        int logi = 0;
        public static AsciiCommands Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (SyncRoot)
                    {
                        if (_instance == null)
                        {
                            _instance = new AsciiCommands();
                        }
                    }
                }

                return _instance;
            }
        }

        private AsciiCommands()
        {
            foreach (AsciiMode am in Utils.GetAllEnumValues<AsciiMode>())
            {
                AsciiModes.Add(am);
            }
        }

        private string _AsciiMessage;

        public string AsciiMessage
        {
            get => _AsciiMessage;
            set { SetProperty(ref _AsciiMessage, value); }
        }
        private string _InFilePath;
        public string InFilePath
        {
            get => _InFilePath;
            set { SetProperty(ref _InFilePath, value); }
        }
        private string _OutFilePath;
        public string OutFilePath
        {
            get => _OutFilePath;
            set { SetProperty(ref _OutFilePath, value); }
        }

        private bool _SplitFrameEnabled;
        public bool SplitFrameEnabled
        {
            get => _SplitFrameEnabled;
            set { SetProperty(ref _SplitFrameEnabled, value); }
        }

        private bool _DoAsciiEnabled;
        public bool DoAsciiEnabled
        {
            get => _DoAsciiEnabled;
            set { SetProperty(ref _DoAsciiEnabled, value); }
        }

        private bool _MergeFrameEnabled;
        public bool MergeFrameEnabled
        {
            get => _MergeFrameEnabled;
            set { SetProperty(ref _MergeFrameEnabled, value); }
        }

        private ObservableCollection<AsciiMode> _AsciiModes = new ObservableCollection<AsciiMode>();

        public ObservableCollection<AsciiMode> AsciiModes
        {
            get => _AsciiModes;
            set { SetProperty(ref _AsciiModes, value); }
        }
        
        private AsciiMode asciiMode = AsciiMode.Black;

        private ICommand _OpenDialogCommand;
        private ICommand _SplitFrameCommand;
        private ICommand _DoAsciiCommand;
        private ICommand _MergeFrameCommand;
        private ICommand _ChangeAsciiModeCommand;
        private ICommand _ChangeAsciiRatioCommand;
        
        public ICommand ChangeAsciiModeCommand =>
            _ChangeAsciiModeCommand
            ?? (_ChangeAsciiModeCommand = new RelayCommand<object>(o => {
            
            asciiMode = (AsciiMode)o;
            }));

        public ICommand OpenDialogCommand =>
            _OpenDialogCommand ?? (_OpenDialogCommand = new RelayCommand<object>(o => {

                OpenFileDialog openFileDialog = new OpenFileDialog();
                string filter = "视频文件(*.mp4,*.mkv,*.mov)|*.mp4;*.mkv;*.mov";

                openFileDialog.Filter = filter;
                openFileDialog.RestoreDirectory = true;
                openFileDialog.FilterIndex = 1;
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string filePath = openFileDialog.FileName;
                    FileInfo fileInfo = new FileInfo(filePath);
                    string name = fileInfo.Name;
                    InFilePath = filePath;
                    string asciiPath = filePath;
                    foreach (string s in new string[] { ".mp4", ".mov", ".mkv" })
                    {
                        asciiPath = asciiPath.Replace(s, "__ascii.mp4");
                    }
                    OutFilePath = asciiPath;
                    SplitFrameEnabled = true;
                    DoAsciiEnabled = false;
                    MergeFrameEnabled = false;
                }
                else
                {
                    return;
                }
            }));

        public ICommand SplitFrameCommand =>
            _SplitFrameCommand ?? (_SplitFrameCommand = new RelayCommand<object>(o => {
                string path = InFilePath;
                FileInfo f = new FileInfo(path);
                DoAsciiEnabled = false;
                MergeFrameEnabled = false;
                if (!f.Exists)
                {
                    AsciiMessage = "请确认是否存在 " + path;
                    return;
                }
                if (string.IsNullOrEmpty(OutFilePath))
                {
                    AsciiMessage = "输出文件不能为空";
                    return;
                }
                if (!OutFilePath.EndsWith(".mp4"))
                {
                    AsciiMessage = "输出文件必须是mp4";
                    return;
                }
                AsciiMessage = OutFilePath;

                //this.mainForm.ShowInfoDialog(f.Name);
                Process process = new Process();
                process.StartInfo.FileName = "ffmpeg";
                //process.StartInfo.WorkingDirectory = path;
                process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                string param = "-i \"" + path + "\" -y -f image2 \"" + Directory.GetCurrentDirectory() + "/cache/%5d.jpg\"";
                Log.Information("ffmpeg " + param);
                //将输出信息重定向
                process.StartInfo.Arguments = param;
                process.StartInfo.CreateNoWindow = true;//显示命令行窗口
                                                        //不使用操作系统使用的shell启动进程
                process.StartInfo.UseShellExecute = false;
                //将输出信息重定向
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.EnableRaisingEvents = true;

                process.Exited += new EventHandler(p_Exited);
                process.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
                process.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);

                process.Start();
                //开始异步读取输出
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();


            }));

        public ICommand DoAsciiCommand =>
            _DoAsciiCommand ?? (_DoAsciiCommand = new RelayCommand<object>(o => {
                          MergeFrameEnabled = false;
                VideoToTxtJpg(asciiMode);
                SplitFrameEnabled = true;
                DoAsciiEnabled = true;
                MergeFrameEnabled = true;
                
            }));

        public ICommand MergeFrameCommand =>
            _MergeFrameCommand ?? (_MergeFrameCommand = new RelayCommand<object>(o => {

                Process process = new Process();
                process.StartInfo.FileName = "ffmpeg";
                //process.StartInfo.WorkingDirectory = path;
                process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                //ffmpeg -f image2 -framerate 30 -i e:\\test\\%d.jpg e:\\test.mp4



                string param = " -f image2 ";
                param += " -framerate " + xfps;
                param += " -i \"" + Directory.GetCurrentDirectory() + "/cache/output/%5d.jpg\" ";
                param += " \"" + OutFilePath + "\"";
                Log.Information("ffmpeg " + param);
                //将输出信息重定向
                process.StartInfo.Arguments = param;
                process.StartInfo.CreateNoWindow = true;//显示命令行窗口
                                                        //不使用操作系统使用的shell启动进程
                process.StartInfo.UseShellExecute = false;
                //将输出信息重定向
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.EnableRaisingEvents = true;

                process.Exited += new EventHandler(p_ExitedMerge);
                process.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceivedMerge);
                process.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceivedMerge);

                process.Start();
                //开始异步读取输出
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
            }));
        public static void ThreadIt(Action action)
        {
            Thread t = new Thread(() => action());
            t.IsBackground = true;
            t.Start();
        }
        // 将像素转换为对应的ASCII字符
        private static char GetChar(int r, int g, int b, int alpha = 256)
        {
            if (alpha == 0)
                return ' ';

            int length = asciiChars.Count;
            int gray = (int)(0.2126 * r + 0.7152 * g + 0.0722 * b);
            double unit = (256.0 + 1) / length;
            return asciiChars[(int)(gray / unit)];
        }

        // 处理单个图像文件，转换为ASCII艺术
        private static async Task Txt2ImageThread(string fileName,AsciiMode am)
        {
            await semaphore.WaitAsync();
            try
            {
                Console.WriteLine($"开始处理ASCII图片: {fileName}");
                string outfile = Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "output"), new FileInfo(fileName).Name);
                using (Bitmap image = new Bitmap(fileName))
                {
                    int originalWidth = image.Width;
                    int originalHeight = image.Height;

                    // 计算调整后的尺寸
                    int width = (int)(originalWidth / 6);
                    int height = (int)(originalHeight / 15);

                    // 调整图像大小
                    using (Bitmap resizedImage = new Bitmap(image, new Size(width, height)))
                    {
                        string txt = "";
                        List<Color> colors = new List<Color>();

                        // 遍历每个像素，生成ASCII文本和颜色信息
                        for (int y = 0; y < height; y++)
                        {
                            for (int x = 0; x < width; x++)
                            {
                                Color pixel = resizedImage.GetPixel(x, y);
                                colors.Add(am == AsciiMode.Colour ? pixel : Color.Black);
                                txt += GetChar(pixel.R, pixel.G, pixel.B);
                            }
                            txt += Environment.NewLine;
                            colors.Add(Color.White); // 行结束符颜色
                        }

                        // 创建用于绘制ASCII艺术的新图像
                        using (Bitmap txtImage = new Bitmap(originalWidth, originalHeight))
                        using (Graphics g = Graphics.FromImage(txtImage))
                        {
                            g.Clear(Color.White);

                            // 使用默认字体
                            Font font = SystemFonts.DefaultFont;
                            float fontWidth = g.MeasureString("W", font).Width;
                            float fontHeight = g.MeasureString("W", font).Height * 1.37f; // 调整后更佳

                            // 绘制ASCII字符
                            float xPos = 0, yPos = 0;
                            int colorIndex = 0;

                            foreach (char c in txt)
                            {
                                if (c == '\n')
                                {
                                    yPos += fontHeight;
                                    xPos = 0;
                                }
                                else
                                {
                                    using (SolidBrush brush = new SolidBrush(colors[colorIndex]))
                                    {
                                        g.DrawString(c.ToString(), font, brush, xPos, yPos);
                                    }
                                    xPos += fontWidth;
                                    colorIndex++;
                                }
                            }

                            // 调整回原始大小并保存
                            using (Bitmap finalImage = new Bitmap(txtImage, originalWidth, originalHeight))
                            {
                                finalImage.Save(outfile, ImageFormat.Jpeg);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理图像时出错: {ex.Message}");
            }
            finally
            {
                semaphore.Release();
            }
        }
        // ASCII字符集，用于像素到字符的映射
        private static readonly List<char> asciiChars = new List<char>(
            "$B%314567890*WM#oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:oa+>!:+. ".ToCharArray());

        // 线程信号量，限制并发线程数量
        private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(10);
        // 将视频分解为图片并处理
        private static void VideoToTxtJpg(AsciiMode am)
        {
            if (!Directory.Exists("Cache"))
            {
                Console.WriteLine("不存在Cache目录，退出");
                return;
            }

            Directory.SetCurrentDirectory("Cache");

            // 获取所有图像文件
            string[] imageFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.*", SearchOption.TopDirectoryOnly)
                .Where(f => f.ToLower().EndsWith(".jpg") || f.ToLower().EndsWith(".png") || f.ToLower().EndsWith(".bmp")).ToArray();

            // 创建任务列表
            List<Task> tasks = new List<Task>();

            // 为每个图像文件创建处理任务
            foreach (string file in imageFiles)
            {
                tasks.Add(Txt2ImageThread(file, am));
            }

            // 等待所有任务完成
            Task.WaitAll(tasks.ToArray());

            Console.WriteLine("处理完毕ASCII图片");
            Directory.SetCurrentDirectory("..");
        }

        // 递归删除目录
        private static void RemoveDirectory(string path)
        {
            if (Directory.Exists(path))
            {
                foreach (string directory in Directory.GetDirectories(path))
                {
                    RemoveDirectory(directory);
                }

                foreach (string file in Directory.GetFiles(path))
                {
                    File.Delete(file);
                }

                Directory.Delete(path);
            }
            else if (File.Exists(path))
            {
                File.Delete(path);
            }
        }
        void p_OutputDataReceived(Object sender, DataReceivedEventArgs e)
        {
            //这里是正常的输出
            Log.Debug("out " + e.Data);
            SplitFrameEnabled = true;
            DoAsciiEnabled = true;
            MergeFrameEnabled = false;
            AsciiMessage = "完成对视频抽帧生成图片";
        }



        void p_ErrorDataReceived(Object sender, DataReceivedEventArgs e)
        {

            AsciiMessage = "正在对视频抽帧生成图片";

            //这里得到的是错误信息
            string info = e.Data;
            logi++;
            Log.Debug(logi + "error " + e.Data);
            if (info != null && info.Contains("fps") && info.Contains("Stream ") && info.Contains("Stream "))
            {
                Log.Information("找到" + info);
                //Stream #0:0(eng): Video: mjpeg, yuvj420p(pc, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 25 fps, 25 tbn (default)
                int ido = info.IndexOf("fps");
                xfps = info.Substring(ido - 4, 4);
                Log.Information("xfps" + xfps);

            }
            //frame=   76 fps=0.0 q=24.8 Lsize=N/A time=00:00:03.00 bitrate=N/A speed=5.21x
            if (info != null && info.Contains("frame") && info.Contains("fps") && info.Contains("size") && info.Contains("time") && info.Contains("bitrate") && info.Contains("speed"))
            {
                string r = "^frame.+time=(.+)bitrate=.+";
                Match match = Regex.Match(info, r);
                if (match.Success)
                {
                    string time = match.Groups[1].Value;
                    if (time.Contains("."))
                    {
                        time = time.Split(".")[0];
                    }
                    string[] times = time.Split(":");
                    int cur = int.Parse(times[0]) * 60 * 60 + int.Parse(times[1]) * 60 + int.Parse(times[2]);
                    AsciiMessage = "对视频抽帧生成图片    当前时长：" + cur + "秒";
                }
            }
        }

        void p_Exited(Object sender, EventArgs e)
        {

        }

        void p_OutputDataReceivedMerge(Object sender, DataReceivedEventArgs e)
        {
            //这里是正常的输出
            Log.Debug("out " + e.Data);
            SplitFrameEnabled = true;
            DoAsciiEnabled = true;
            MergeFrameEnabled = false;
        }


        void p_ErrorDataReceivedMerge(Object sender, DataReceivedEventArgs e)
        {
            Log.Debug("error " + e.Data);
            AsciiMessage = "正在对视频抽帧生成图片";

            //这里得到的是错误信息
            string info = e.Data;
            //frame=   76 fps=0.0 q=24.8 Lsize=N/A time=00:00:03.00 bitrate=N/A speed=5.21x
            if (info != null && info.Contains("frame") && info.Contains("fps") && info.Contains("size") && info.Contains("time") && info.Contains("bitrate") && info.Contains("speed"))
            {
                string r = "^frame.+time=(.+)bitrate=.+";
                Match match = Regex.Match(info, r);
                if (match.Success)
                {
                    string time = match.Groups[1].Value;
                    if (time.Contains("."))
                    {
                        time = time.Split(".")[0];
                    }
                    string[] times = time.Split(":");
                    int cur = int.Parse(times[0]) * 60 * 60 + int.Parse(times[1]) * 60 + int.Parse(times[2]);
                    AsciiMessage = "对视频抽帧生成图片    当前时长：" + cur + "秒";
                }
                AsciiMessage = "完成对视频抽帧生成图片";
            }
        }

        void p_ExitedMerge(Object sender, EventArgs e)
        {

        }

    }
}
